﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathNet.Numerics.Distributions;
using FN = MathNet.Numerics.Fn;

namespace AMOOF {

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    /// 
    //===Beta===
    public class Beta : PDF {
        BetaDistribution dist;
        public Beta() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.BETA, 1);
            Greeks.Add(Greek.THETA, 1);
            //greeks.Add(new Variable { type = Greek.ALPHA, value = 1 });
            //greeks.Add(new Variable { type = Greek.BETA, value = 1 });
            //greeks.Add(new Variable { type = Greek.THETA, value = 1 });
            dist = new BetaDistribution();
            lnFunction = "(GAMMALN(<ALPHA>+<BETA>)-GAMMALN(<ALPHA>)-GAMMALN(<BETA>)+<ALPHA>*LN(<X>/<THETA>)+(<BETA>-1)*LN(1-<X>/<THETA>)-LN(<X>))*<FREQUENCY>"; 
            function = "((EXP(GAMMALN(<ALPHA>+<BETA>))/(EXP(GAMMALN(<ALPHA>)+GAMMALN(<BETA>))))*(((<x>/<THETA>)^<ALPHA>)*(1-(<x>/<THETA>))^(<BETA>-1))/<x>)"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>+<BETA>)+GAMMALN(<ALPHA>+<K>)-GAMMALN(<ALPHA>)-GAMMALN(<ALPHA>+<BETA>+<K>))";
            name = "Beta";
        }
        public override double getOutput(double x) {
            if (x >= Greeks[Greek.ALPHA] || x <= 0) {
                return 0;
            }
            double u = x / Greeks[Greek.THETA];
            double num = FN.Gamma(Greeks[Greek.ALPHA] + Greeks[Greek.BETA]) * Math.Pow(u, Greeks[Greek.ALPHA])
                * Math.Pow(1 - u, Greeks[Greek.BETA] - 1);
            double denom = FN.Gamma(Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.BETA]) * x;
            return num / denom;
        }
        public override double getMoment(double k) {
            return 0;
        }
    }

    //===burr===
    public class Burr : PDF {
        public Burr() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.GAMMA, 1);
            lnFunction = "(LN(<ALPHA>)+LN(<GAMMA>) + <GAMMA>*LN(<X>/<THETA>)-LN(<X>)-(<ALPHA>+1)*LN(1+(<X>/<THETA>)^(<GAMMA>)))*<FREQUENCY>"; 
            function = "(<ALPHA>*<GAMMA>*(<X>/<THETA>)^<GAMMA>)/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^(<ALPHA>+1))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1+<K>/<GAMMA>)+GAMMALN(<ALPHA>-<K>/<GAMMA>)-GAMMALN(<ALPHA>))";
            name = "Burr";
        }
        public override double getOutput(double x) {
            double numerator = Greeks[Greek.ALPHA] * Greeks[Greek.GAMMA] * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]);
            double denominator = x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]), Greeks[Greek.ALPHA] + 1);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            return 0;
        }
    }

    //===Exponential===
    public class Exponential : PDF {
        ExponentialDistribution dist;
        public Exponential() {
            Greeks.Add(Greek.THETA, 1);
            dist = new ExponentialDistribution();
            lnFunction = "(-<X>/EXP(1) - LN(<THETA>))*<FREQUENCY>";
            function = "(EXP(-<X>/<THETA>)/<THETA>)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<K>+1))";
            name = "Exponential";
        }
        public override double getOutput(double x) {
            return Math.Exp(-x / Greeks[Greek.THETA]) / Greeks[Greek.THETA];
        }
        public override double getMoment(double k) {
            if (k > -1) {
                return Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(k + 1);
            }
            return -1;
        }
    }

    //===Gamma===
    public class Gamma : PDF {
        GammaDistribution gammaDist;
        public Gamma() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            gammaDist = new GammaDistribution();
            lnFunction = "(<ALPHA>*LN(<X>/<THETA>)+-<X>/EXP(1)-LN(<X>)-GAMMALN(<ALPHA>))*<FREQUENCY>"; 
            function = "((<X>/<THETA>)^<ALPHA>)*EXP(-<X>/<THETA>)/(<X>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>+<K>)-GAMMALN(<ALPHA>))";
            name = "Gamma";
        }
        public override double getOutput(double x) {
            double numerator = Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.ALPHA]) * Math.Exp(-x / Greeks[Greek.THETA]);
            double denominator = x * FN.Gamma(Greeks[Greek.ALPHA]);
            double val = numerator / denominator;
            return val;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.ALPHA]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.ALPHA] + k);
            double denominator = FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denominator;
        }
    }

    // ===Generalized Beta===
    public class GeneralizedBeta : PDF {
        public GeneralizedBeta() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.BETA, 1);
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.TAU, 1);
            lnFunction = "(GAMMALN(<ALPHA>+<BETA>)-GAMMALN(<ALPHA>)-GAMMALN(<BETA>)+<ALPHA>*<TAU>*LN(<X>/<THETA>)+(<BETA>-1)*LN(1-(<X>/<THETA>)^(<TAU>))+LN(<TAU>/<X>))*<FREQUENCY>";
            function = "(EXP(GAMMALN(<ALPHA>+<BETA>))/(EXP(GAMMALN(<ALPHA>))*EXP(GAMMALN(<BETA>)))*((<x>/<THETA>)^<TAU>)^<ALPHA>*(1-((<x>/<THETA>)^<TAU>))^(<BETA>-1)*<TAU>/<x>)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>+<BETA>)+GAMMALN(<ALPHA>+<K>/<TAU>)-GAMMALN(<ALPHA>)-GAMMALN(<ALPHA>+<BETA>+<K>/<TAU>))";
            name = "Generalized Beta";
        }
        public override double getOutput(double x) {
            if (x >= Greeks[Greek.THETA] || x <= 0) {
                return 0;
            }
            double u = Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.TAU]);
            double numerator = FN.Gamma(Greeks[Greek.ALPHA] + Greeks[Greek.BETA]) * Math.Pow(u, Greeks[Greek.ALPHA]) * Math.Pow(1 - u, Greeks[Greek.BETA] - 1) * Greeks[Greek.TAU];
            double denominator = FN.Gamma(Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.BETA]) * x;
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            return 0;
        }
    }

    // ===Generalized Pareto===
    public class GeneralizedPareto : PDF {
        public GeneralizedPareto() {
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.GAMMA, 1);
            lnFunction = "(LN(1/<GAMMA>)+((1/<GAMMA>)*LN(<THETA>/<GAMMA>))-(1+(1/<GAMMA>))*LN(<X>+(<THETA>/<GAMMA>)))*<FREQUENCY>"; 
            function = "(((1/<GAMMA>)*((<THETA>/<GAMMA>)^(1/<GAMMA>)))/((X+(<THETA>/<GAMMA>))^(1+(1/<GAMMA>))))*<FREQUENCY>"; 
            moment = "((<THETA>/<GAMMA>)^<K>)*EXP(GAMMALN(<K>+1)+GAMMALN((1/<GAMMA>)-<K>)-GAMMALN(1/<GAMMA>))";
            name = "Generalized Pareto";
        }
        public override double getOutput(double x) {
            double numerator = (1 / Greeks[Greek.GAMMA]) * Math.Pow(Greeks[Greek.THETA] / Greeks[Greek.GAMMA], 1 / Greeks[Greek.GAMMA]);
            double denominator = Math.Pow(x + Greeks[Greek.THETA] / Greeks[Greek.GAMMA], 1 / Greeks[Greek.GAMMA] + 1);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.TAU] || k >= Greeks[Greek.ALPHA]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.TAU] + k) * FN.Gamma(Greeks[Greek.ALPHA] - k);
            double denominator = FN.Gamma(Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.TAU]);
            return numerator / denominator;
        }
    }

    //===Inverse Burr===
    public class InverseBurr : PDF {
        public InverseBurr() {
            Greeks.Add(Greek.TAU, 1);
            Greeks.Add(Greek.GAMMA, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<TAU>)+LN(<GAMMA>)+ <GAMMA>*<TAU>*LN(<X>/<THETA>)-LN(<X>)-(<TAU>+1)*LN(1+(<X>/<THETA>)^<GAMMA>))*<FREQUENCY>";
            function = "(<TAU>*<GAMMA>*(<x>/<THETA>)^(<GAMMA>*<TAU>))/(<x>*(1+(<x>/<THETA>)^<GAMMA>)^(<TAU>+1))*<FREQUENCY>";
            moment = "<THETA>^<K>*EXP(GAMMALN(<TAU>+<K>/<GAMMA>)+GAMMALN(1-<K>/<GAMMA>)-GAMMALN(<TAU>))";
            name = "Inverse Burr";
        }
        public override double getOutput(double x) {
            double numerator = Greeks[Greek.TAU] * Greeks[Greek.GAMMA] * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.TAU] * Greeks[Greek.GAMMA]);
            double denominator = x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]), Greeks[Greek.TAU] + 1);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.TAU] * Greeks[Greek.GAMMA] || k >= Greeks[Greek.GAMMA]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.GAMMA], k) * (Greeks[Greek.TAU] + k / Greeks[Greek.GAMMA]) * FN.Gamma(1 - k / Greeks[Greek.GAMMA]);
            double denominator = FN.Gamma(Greeks[Greek.TAU]);
            return numerator / denominator;
        }
    }

    //===Inverse Exponential===
    public class InverseExponential : PDF {
        public InverseExponential() {
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<THETA>)-<THETA>/<X>-2*LN(<X>))*<FREQUENCY>";
            function = "(<THETA>*EXP(-<THETA>/<X>))/(<X>^2)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1-<K>))";
            name = "Inverse Exponential";
        }
        public override double getOutput(double x) {
            double numerator = Greeks[Greek.THETA] * Math.Exp(-Greeks[Greek.THETA] / x);
            double denominator = Math.Pow(x, 2);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k >= 1) {
                return 0;
            }
            return Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(1 - k);
        }
    }

    //===Inverse Gamma===
    public class InverseGamma : PDF {
        public InverseGamma() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(<ALPHA>*LN(<THETA>/<X>)-LN(<X>)-GAMMALN(<ALPHA>))*<FREQUENCY>"; 
            function = "((<THETA>/<X>)^<ALPHA>)*EXP(-<THETA>/<X>)/(<X>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>-<K>)-GAMMALN(<ALPHA>))";
            name = "Inverse Gamma";
        }
        public override double getOutput(double x) {
            double num = Math.Pow(Greeks[Greek.THETA] / x, Greeks[Greek.ALPHA]) * Math.Exp(-Greeks[Greek.THETA] / x);
            double denom = x * FN.Gamma(Greeks[Greek.ALPHA]);
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k >= Greeks[Greek.ALPHA]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.ALPHA] - k);
            double denomerator = FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denomerator;
        }
    }

    //===Inverse Gaussian===
    public class InverseGaussian : PDF {
        public string moment1;
        public string moment2;
        public string moment3;
        public string moment4;
        public InverseGaussian() {
            Greeks.Add(Greek.MU, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(1/2*(LN(<THETA>)-LN(2*PI())-3*LN(<X>))-<THETA>*(((<X>-<MU>)/<MU>)^2)/(2*<X>))*<FREQUENCY>";
            function = "(<THETA>/(2*PI()*<X>^3))^(1/2)*EXP(-(<THETA>*((<X>-<MU>)/<MU>)^2)/(2*<X>))*<FREQUENCY>"; 
            moment = "VALID MOMENTS ARE DEFINED IN moment1...moment4, BECAUSE SINGLE FORMULA IS UNREALISTIC FOR EXCEL USE";
            name = "Inverse Gaussian";
        }
        public override double getOutput(double x) {
            double z = (x - Greeks[Greek.MU]) / Greeks[Greek.MU];
            double a = Math.Pow(Greeks[Greek.THETA] / (2 * Math.PI * Math.Pow(x, 3)), 1 / 2);
            double b = Math.Exp(-Greeks[Greek.THETA] * Math.Pow(z, 2) / (2 * x));
            return a * b;
        }
        public override double getMoment(double k) {
            return Greeks[Greek.MU]; // documentation only specifies the first moment
        }
    }

    //===Inverse Paralogistic===
    public class InverseParalogistic : PDF {
        public InverseParalogistic() {
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.TAU, 1);
            lnFunction = "(2*LN(<TAU>)+2*<TAU>*LN(<X>/<THETA>)-LN(<X>)-(<TAU>+1)*LN(1+(<X>/<THETA>)^<TAU>))*<FREQUENCY>"; 
            function = "((<TAU>^2)*(<X>/<THETA>)^(<TAU>^2))/(<X>*(1+(<X>/<THETA>)^<TAU>)^(<TAU>+1))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<TAU>+<K>/<TAU>)+GAMMALN(1-<K>/<TAU>)-GAMMALN(<TAU>))";
            name = "Inverse Paralogistic";
        }
        public override double getOutput(double x) {
            double numerator = Math.Pow(Greeks[Greek.TAU], 2) * Math.Pow(x / Greeks[Greek.THETA], Math.Pow(Greeks[Greek.TAU], 2));
            double denominator = x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.TAU]), Greeks[Greek.TAU] + 1);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            return 0;
        }
    }

    //===Inverse Pareto===
    public class InversePareto : PDF {
        public InversePareto() {
            Greeks.Add(Greek.TAU, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<TAU>)+LN(<THETA>)+(<TAU>-1)*LN(<X>)-(<TAU>+1)*LN(<X>+<THETA>))*<FREQUENCY>";
            function = "(<TAU>*<THETA>*(<X>^(<TAU> - 1))/((<X>+<THETA>)^(<TAU>+1)))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<TAU>+<K>)+GAMMALN(1-<K>)-GAMMALN(<TAU>))";
            name = "Inverse Pareto";
        }
        public override double getOutput(double x) {
            double num = Greeks[Greek.TAU] * Greeks[Greek.THETA] * Math.Pow(x, Greeks[Greek.TAU] - 1);
            double denom = Math.Pow(x + Greeks[Greek.THETA], Greeks[Greek.TAU] + 1);
            return num / denom;
        }
        public override double getMoment(double k) {
            if (-Greeks[Greek.TAU] >= k || k >= 1) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.TAU] + k) * FN.Gamma(1 - k);
            double denomerator = FN.Gamma(Greeks[Greek.TAU]);
            return numerator / denomerator;
        }
    }

    //===Inverse Transformed Gamma===
    public class InverseTransformedGamma : PDF {
        public InverseTransformedGamma() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.TAU, 1);
            lnFunction = "(LN(<TAU>)+<ALPHA>*<TAU>*LN(<THETA>/<X>)-(<THETA>/<X>)^<TAU>-LN(<X>)-GAMMALN(<ALPHA>))*<FREQUENCY>"; 
            function = "(<TAU>*((<THETA>/<X>)^(<ALPHA>*<TAU>))*EXP(-((<THETA>/<x>)^<TAU>)))/(<x>*EXP(GAMMALN(<ALPHA>)))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>-<K>/<TAU>)-GAMMALN(<ALPHA>))";
            name = "Inverse Transformed Gamma";
        }
        public override double getOutput(double x) {
            double u = Math.Pow(Greeks[Greek.THETA] / x, Greeks[Greek.TAU]);
            double numerator = Greeks[Greek.TAU] * Math.Pow(u, Greeks[Greek.ALPHA]) * Math.Exp(-u);
            double denominator = x * FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k >= Greeks[Greek.ALPHA] * Greeks[Greek.TAU]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.ALPHA] - k / Greeks[Greek.TAU]);
            double denomerator = FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denomerator;
        }
    }

    //===Inverse Weibull==
    public class InverseWeibull : PDF {
        public InverseWeibull() {
            Greeks.Add(Greek.TAU, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<TAU>)+<TAU>*LN(<THETA>/<X>)-(<THETA>/<X>)^<TAU>-LN(<X>))*<FREQUENCY>"; 
            function = "(<TAU>*((<THETA>/<X>)^<TAU>)*EXP(-((<THETA>/<X>)^<TAU>))/<X>)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1-<K>/<TAU>))";
            name = "Inverse Weibull";
        }
        public override double getOutput(double x) {
            double num = Greeks[Greek.TAU] * Math.Pow(Greeks[Greek.THETA] / x, Greeks[Greek.TAU]) * Math.Exp(Math.Pow(Greeks[Greek.THETA] / x, Greeks[Greek.TAU]));
            double denom = x;
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k >= Greeks[Greek.TAU]) {
                return 0;
            }
            return Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(1 - k / Greeks[Greek.TAU]);
        }
    }

    //===Loglogistic===
    public class Loglogistic : PDF {
        public Loglogistic() {
            Greeks.Add(Greek.GAMMA, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<GAMMA>)+<GAMMA>*LN(<X>/<THETA>)-LN(<X>)-2*LN(1+(<X>/<THETA>)^<GAMMA>))*<FREQUENCY>"; 
            function = "(<GAMMA>*((<X>/<THETA>)^<GAMMA>)/(<X>*(1+(<X>/<THETA>)^<GAMMA>)^2))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1+<K>/<GAMMA>)+GAMMALN(1-<K>/<GAMMA>))";
            name = "Loglogistic";
        }
        public override double getOutput(double x) {
            if (x > 10000000)
            {
                int s = 0;
            }
            double num = Greeks[Greek.GAMMA] * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]);
            double denom = x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]), 2);
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.GAMMA] || k >= Greeks[Greek.GAMMA]) {
                return 0;
            }
            return Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(1 + k / Greeks[Greek.GAMMA]) * FN.Gamma(1 - k / Greeks[Greek.GAMMA]);
        }
    }

    //===Lognormal===
    public class Lognormal : PDF {
        LognormalDistribution logDist;

        public Lognormal() {
            Greeks.Add(Greek.MU, 12.3254344);
            Greeks.Add(Greek.SIGMA, 16.00231939);
            logDist = new LognormalDistribution();
            lnFunction = "(-((LN(<X>)-<MU>)/<SIGMA>)/2-LN(<X>)-LN(<SIGMA>)-1/2*LN(2*PI()))*<FREQUENCY>"; 
            function = "(1/(<X>*<SIGMA>*SQRT(2*PI())))*EXP(-(((LOG(<X>)-<MU>)/<SIGMA>)^2)/2)*<FREQUENCY>"; 
            moment = "EXP(<K>*<MU>+((<K>^2*<SIGMA>^2)/2))";
            name = "Lognormal";
        }
        public override double getOutput(double x) {
            if (x == 0) {
                return 0;
            }
            double test1 = Math.Log(x);
            double test3 = Greeks[Greek.SIGMA];
            double z = (Math.Log(x, Math.E) - Greeks[Greek.MU]) / Greeks[Greek.SIGMA];
            double a = Math.Pow(-z, 2.0);
            double b = Math.Exp(a / 2.0);
            double num = Math.Exp(Math.Pow(-z, 2) / 2);
            double denom = x * Greeks[Greek.SIGMA] * Math.Sqrt(2 * Math.PI);
            double val = num / denom;
            return val;
        }
        public override double getMoment(double k) {
            return Math.Exp(k * Greeks[Greek.MU] + Math.Pow(k, 2) * Math.Pow(Greeks[Greek.SIGMA], 2) / 2);
        }
    }

    //===Paralogistic===
    public class Paralogistic : PDF {
        public Paralogistic() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(2*LN(<ALPHA>)+<ALPHA>*LN(<X>/<THETA>)-LN(<X>)-(<ALPHA>+1)*LN(1+(<X>/<THETA>)^<ALPHA>))*<FREQUENCY>"; 
            function = "((<ALPHA>^2)*(<x>/<THETA>)^<ALPHA>)/(<x>*(1+(<x>/<THETA>)^<ALPHA>)^(<ALPHA>+1))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1+<K>/<ALPHA>)+GAMMALN(<ALPHA>-<K>/<ALPHA>)-GAMMALN(<ALPHA>))";
            name = "Paralogistic";
        }
        public override double getOutput(double x) {
            double num = Math.Pow(Greeks[Greek.ALPHA], 2) * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.ALPHA]);
            double denom = x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.ALPHA]), Greeks[Greek.ALPHA] + 1);
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.ALPHA] || k >= Math.Pow(Greeks[Greek.ALPHA], 2)) {
                return 0;
            }

            double numerator = Math.Pow(Greeks[Greek.THETA], k) * (1 + k / Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.ALPHA] - k / Greeks[Greek.ALPHA]);
            double denomerator = FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denomerator;
        }
    }

    //===Pareto===
    public class Pareto : PDF {
        ParetoDistribution dist;

        public Pareto() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            dist = new ParetoDistribution();
            lnFunction = "(LN(<ALPHA>)+<ALPHA>*LN(<THETA>)-(<ALPHA>+1)*LN(<X>+<THETA>))*<FREQUENCY>";
            function = "(<ALPHA>*<THETA>^<ALPHA>)/(<X>+<THETA>)^(<ALPHA>+1)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<K>+1)+GAMMALN(<ALPHA>-<K>)-GAMMALN(<ALPHA>))";
            name = "Pareto";
        }
        public override double getOutput(double x) {
            double numerator = Greeks[Greek.ALPHA] * Math.Pow(Greeks[Greek.THETA], Greeks[Greek.ALPHA]);
            double denominator = Math.Pow(x + Greeks[Greek.THETA], Greeks[Greek.ALPHA] + 1);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            return 0;
        }
    }

    // ===Single Parameter Pareto===
    public class SingleParameterPareto : PDF {
        public SingleParameterPareto() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            lnFunction = "(LN(<ALPHA>)+<ALPHA>*LN(<THETA>)-(<ALPHA>+1)*LN(<X>))*<FREQUENCY>"; 
            function = "(<ALPHA>*<THETA>^<ALPHA>)/<x>^(<ALPHA>+1)*<FREQUENCY>"; 
            moment = "<ALPHA>*<THETA>^<K>/(<ALPHA>-<K>)";
            name = "Single Parameter Pareto";
        }
        public override double getOutput(double x) {
            if (x <= Greeks[Greek.THETA]) {
                return 0;
            }
            double num = Greeks[Greek.ALPHA] * Math.Pow(Greeks[Greek.THETA], Greeks[Greek.ALPHA]);
            double denom = Math.Pow(x, Greeks[Greek.ALPHA] + 1);
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k >= Greeks[Greek.ALPHA]) {
                return 0;
            }
            double numerator = Greeks[Greek.ALPHA] * Math.Pow(Greeks[Greek.THETA], k);
            double denomerator = Greeks[Greek.ALPHA] - k;
            return numerator / denomerator;
        }
    }

    //===Transformed Beta===
    public class TransformedBeta : PDF {
        public TransformedBeta() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.TAU, 1);
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.GAMMA, 1);
            lnFunction = "(GAMMALN(<ALPHA>+<TAU>)-GAMMALN(<ALPHA>)-GAMMALN(<TAU>)+LN(<GAMMA>)+<GAMMA>*<TAU>*LN(<X>/<THETA>)-LN(<X>)-(<ALPHA>+<TAU>)*LN(1+(<X>/<THETA>)^<GAMMA>))*<FREQUENCY>"; 
            function = "(EXP(GAMMALN(<ALPHA>+<TAU>))/EXP(GAMMALN(<ALPHA>)+GAMMALN(<TAU>)))*(<GAMMA>*(<x>/<THETA>)^(<GAMMA>*<TAU>))/(<x>*(1+(<x>/<THETA>)^<GAMMA>)^(<ALPHA>+<TAU>))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<TAU>+<K>/<GAMMA>)+GAMMALN(<ALPHA>-<K>/<GAMMA>)-GAMMALN(<ALPHA>)-GAMMALN(<TAU>))";
            name = "Transformed Beta";
        }
        public override double getOutput(double x) {
            double numerator = FN.Gamma(Greeks[Greek.ALPHA] + Greeks[Greek.TAU]) * Greeks[Greek.GAMMA] * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA] * Greeks[Greek.TAU]);
            double denominator = FN.Gamma(Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.TAU]) * x * Math.Pow(1 + Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.GAMMA]), Greeks[Greek.ALPHA] + Greeks[Greek.TAU]);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.TAU] * Greeks[Greek.GAMMA] || k >= Greeks[Greek.ALPHA] * Greeks[Greek.GAMMA]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(Greeks[Greek.TAU] + k / Greeks[Greek.GAMMA]) * FN.Gamma(Greeks[Greek.ALPHA] - k / Greeks[Greek.GAMMA]);
            double denominator = FN.Gamma(Greeks[Greek.ALPHA]) * FN.Gamma(Greeks[Greek.TAU]);
            return numerator / denominator;
        }
    }

    // ===Transformed Gamma===
    public class TransformedGamma : PDF {
        public TransformedGamma() {
            Greeks.Add(Greek.ALPHA, 1);
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.TAU, 1);
            lnFunction = "(LN(<TAU>)+<ALPHA>*<TAU>*(LN(<X>)-LN(<THETA>))-(<X>/<THETA>)^<TAU>-LN(<X>)-GAMMALN(<ALPHA>))*<FREQUENCY>"; 
            function = "(<TAU>*((<X>/<THETA>)^(<ALPHA>*<TAU>))*EXP(-((<X>/<THETA>)^<TAU>))/(<X>*EXP(GAMMALN(<ALPHA>))))*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(<ALPHA>+<K>/<TAU>)-GAMMALN(<ALPHA>))";
            name = "Transformed Gamma";
        }
        public override double getOutput(double x) {
            double u = Math.Pow((x / Greeks[Greek.THETA]), Greeks[Greek.TAU]);
            double numerator = Greeks[Greek.TAU] * Math.Pow(u, Greeks[Greek.ALPHA]) * Math.Exp(-u);
            double denominator = x * FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denominator;
        }
        public override double getMoment(double k) {
            if (k <= Greeks[Greek.ALPHA] * Greeks[Greek.TAU]) {
                return 0;
            }
            double numerator = Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma((Greeks[Greek.ALPHA] + k) / Greeks[Greek.TAU]);
            double denomerator = FN.Gamma(Greeks[Greek.ALPHA]);
            return numerator / denomerator;
        }
    }

    //===Weibull===
    public class Weibull : PDF {
        public Weibull() {
            Greeks.Add(Greek.THETA, 1);
            Greeks.Add(Greek.TAU, 1);
            lnFunction = "(LN(<TAU>)+<TAU>*LN(<X>/<THETA>)-(<X>/<THETA>)^<TAU>-LN(<X>))*<FREQUENCY>"; 
            function = "(<TAU>*((<X>/<THETA>)^<TAU>)*EXP(-((<X>/<THETA>)^<TAU>))/<X>)*<FREQUENCY>"; 
            moment = "<THETA>^<K>*EXP(GAMMALN(1+<K>/<TAU>))";
            name = "Weibull";
        }
        public override double getOutput(double x) {
            double num = Greeks[Greek.TAU] * Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.TAU]) * Math.Exp(-Math.Pow(x / Greeks[Greek.THETA], Greeks[Greek.TAU]));
            double denom = x;
            return num / denom;
        }
        public override double getMoment(double k) {
            if (k <= -Greeks[Greek.TAU]) {
                return 0;
            }
            return Math.Pow(Greeks[Greek.THETA], k) * FN.Gamma(1 + k / Greeks[Greek.TAU]);
        }
    }

    // Holds multiple PDFs
    public class MultiplePDF : PDF {
        public List<PDF> pdfs;
        public List<double> scalingFactors;
        public List<Bounds> bounds;
        public List<double> highPoints;
        public int ID;
        public static int count = 0;
        public string periFunctionString = "";
        public MultiplePDF(List<PDF> pfs, List<double> sclingFactors) {
            pdfs = pfs;
            scalingFactors = sclingFactors;
            bounds = new List<Bounds>();
            highPoints = new List<double>();
            name = pfs.ElementAt(0).name; // needs to reflect different combinations
            ID = count;
            count++;
        }

        // This requires disregarding the sub-pdfs, as the program is using upper-level pdfs
        // and not the lower-level pdfs. This should be changed, but would be complex. It would
        // be better to use the low-level pdfs, so that it is not hard-coded, as using the upper-level
        // assumes two of the same type, and no other combinations.
        public string getGreeksString() {
            String str = "";
            int counter1 = 1;
            int counter2 = 1;
            int counter = 0;
            foreach (var kvp in this.Greeks) {
                counter2 = counter1;
                if (kvp.Key.Equals(PDF.Greek.P1) || kvp.Key.Equals(PDF.Greek.P2)) {
                    continue;
                }
                string name = kvp.Key.ToString();
                if (name.Contains("2")) {
                    name = name.Replace("2", "");
                    counter = counter2;
                    counter2++;
                }
                else {
                    counter1++;
                }
                str += name + counter + "".PadRight(5)
                        + kvp.Value + "\r\n";

            }
            for (int i = 0; i < pdfs.Count; i++) {
                //PDF pdf = pdfs.ElementAt(i);
                //foreach (Variable greek in pdf.greeks) {
                //    str += greek.type.ToString() + (i + 1) + "".PadRight(5)
                //        + greek.value + "\r\n";
                //}
            }
            return str;
        }
        public override string getPeriCommand() {
            if (pdfs.ElementAt(0) is MultiplePDF) {
                return pdfs.ElementAt(0).getPeriCommand();
            }
            String str = "";
            str += "\r\nwriteToFile(\"\");";
            str += "\r\nwriteToFile(\"<ID>\");";
            str += "\r\nwriteToFile(\"" + ID + "\");";
            str += "\r\nwriteToFile(\"</ID>\");";
            str += "\r\nwriteToFile(\"\");";
            str += "\r\npdfs = " + pdfs.Count() + ";";
            for (int i = 1; i <= pdfs.Count; i++) {
                String periString = scalingFactors.ElementAt(i - 1)
                    //String periString = .5
                    + "*" + pdfs.ElementAt(i - 1).getPeriCommand();
                str += "\r\ninitialize" + "(\"" + periString + "\"" + "," + i + ");";
            }
            str += "\r\ncalculateAllVARCTEs();";
            return str;
        }

        public override double getMoment(double d) {
            return 1;
        }

        public void addIfNotNull(Greek mainGreek,Greek subGreek,PDF pdf) {
            Boolean exists = false;
            double existing = -1;
            try {
                existing = pdf.Greeks[subGreek];
                exists = true;
            }
            catch (Exception e) { }
            if (exists) {
                pdf.Greeks[subGreek] = Greeks[mainGreek];
            }
        }

        public void transferGreekValues(){
            PDF pdf1 = pdfs.ElementAt(0);
            PDF pdf2 = pdfs.ElementAt(1);
            addIfNotNull(Greek.ALPHA,Greek.ALPHA, pdf1);
            addIfNotNull(Greek.BETA, Greek.BETA, pdf1);
            addIfNotNull(Greek.GAMMA, Greek.GAMMA, pdf1);
            addIfNotNull(Greek.MU, Greek.MU, pdf1);
            addIfNotNull(Greek.SIGMA, Greek.SIGMA, pdf1);
            addIfNotNull(Greek.TAU, Greek.TAU, pdf1);
            addIfNotNull(Greek.THETA, Greek.THETA, pdf1);
            addIfNotNull(Greek.ALPHA2,Greek.ALPHA, pdf2);
            addIfNotNull(Greek.BETA2, Greek.BETA, pdf2);
            addIfNotNull(Greek.GAMMA2, Greek.GAMMA, pdf2);
            addIfNotNull(Greek.MU2, Greek.MU, pdf2);
            addIfNotNull(Greek.SIGMA2, Greek.SIGMA, pdf2);
            addIfNotNull(Greek.TAU2, Greek.TAU, pdf2);
            addIfNotNull(Greek.THETA2, Greek.THETA, pdf2);
        }
        public override double getOutput(double x) {
            double output = 0;
            // this is required, as explained above above getGreeksString()
            // transferGreekValues();

            for (int i = 0; i < pdfs.Count; i++) {
                PDF pdf = pdfs.ElementAt(i);
                double scaling = scalingFactors.ElementAt(i);
                try {
                    double thisOutput = scaling * pdf.getOutput(x);
                    if (Integrator.Integral.numValid(thisOutput)) {
                        output += thisOutput;
                    }
                }
                catch (Exception e) { Program.log(e.Message + "\r\n" + e.StackTrace); }
            }
            return output;
        }
        internal List<Bounds> getBounds() {
            return union(bounds);
        }
        // collapse bounds of where pdf output is valid (used by integrator (not peri))
        private List<Bounds> union(List<Bounds> bounds) {
            List<Bounds> copyList = new List<Bounds>(bounds);
            List<Bounds> finalList = new List<Bounds>();

            copyList.Sort(new Comparison<Bounds>((a, b) => {
                if (a.left - b.left < 0) {
                    return -1;
                }
                return 1;
            }));

            while (copyList.Count > 0) {
                Bounds accumulator = copyList.ElementAt(0);
                finalList.Add(accumulator);
                copyList.RemoveAt(0);
                Boolean done = false;

                while (!done && copyList.Count > 0) {
                    Bounds nextBounds = bounds.ElementAt(0);
                    if (nextBounds.left < accumulator.right) {
                        accumulator.right = nextBounds.right;
                        copyList.RemoveAt(0);
                    }
                    else {
                        done = true;
                    }
                }
            }
            return finalList;
        }

        // get closest highpoint (used by integrator (not peri))
        internal double getDistanceFromHighpoint(double x) {
            double minDist = double.MaxValue;
            foreach (double d in highPoints) {
                double thisDist = Math.Abs(x - d);
                minDist = Math.Min(minDist, thisDist);
            }
            return minDist;
        }
    }
}
